﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="MatrixExtensions.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.Core.
//
//   starLiGHT.Core is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Core is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Core. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Core are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 620                   $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2010-12-07 15:23:58 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Xna.Framework;

    #endregion

    /// <summary>
    /// Extension methods for the Matrix data type
    /// </summary>
    public static class MatrixExtensions
    {
        public static float[] Items(this Matrix m)
        {
            return new float[] 
                               { 
                                 m.M11, m.M12, m.M13, m.M14,
                                 m.M21, m.M22, m.M23, m.M24,
                                 m.M31, m.M32, m.M33, m.M34,
                                 m.M41, m.M42, m.M43, m.M44,
                               };
        }

        public static Vector3 TransformAffine(this Matrix m, Vector3 v)
        {
            return new Vector3(
                               (m.M11 * v.X) + (m.M12 * v.Y) + (m.M13 * v.Z) + m.M14,
                               (m.M21 * v.X) + (m.M22 * v.Y) + (m.M23 * v.Z) + m.M24,
                               (m.M31 * v.X) + (m.M32 * v.Y) + (m.M33 * v.Z) + m.M34);
        }

        public static bool HasScale(this Matrix m)
        {
            float t = (m.M11 * m.M11) + (m.M21 * m.M21) + (m.M31 * m.M31);
            if (!(Math.Abs(1.0f - t) <= 1e-04))
            {
                return true;
            }

            t = (m.M12 * m.M12) + (m.M22 * m.M22) + (m.M32 * m.M32);
            if (!(Math.Abs(1.0f - t) <= 1e-04))
            {
                return true;
            }

            t = (m.M12 * m.M12) + (m.M23 * m.M23) + (m.M33 * m.M33);
            if (!(Math.Abs(1.0f - t) <= 1e-04))
            {
                return true;
            }

            return false;
        }

        public static bool HasNegativeScale(this Matrix m)
        {
            return m.Determinant() < 0;
        }

        public static Vector3 getTrans(this Matrix m)
        {
            return new Vector3(m.M14, m.M24, m.M34);
        }

        public static Matrix setTrans(this Matrix m, Vector3 v)
        {
            m.M14 = v.X;
            m.M24 = v.Y;
            m.M34 = v.Z;

            return m;
        }

        public static float Item(this Matrix m, int x, int y)
        {
            if (x == 0 && y == 0)
            {
                return m.M11;
            }
            else if (x == 0 && y == 1)
            {
                return m.M12;
            }
            else if (x == 0 && y == 2)
            {
                return m.M13;
            }
            else if (x == 0 && y == 3)
            {
                return m.M14;
            }
            else if (x == 1 && y == 0)
            {
                return m.M21;
            }
            else if (x == 1 && y == 1)
            {
                return m.M22;
            }
            else if (x == 1 && y == 2)
            {
                return m.M23;
            }
            else if (x == 1 && y == 3)
            {
                return m.M24;
            }
            else if (x == 2 && y == 0)
            {
                return m.M31;
            }
            else if (x == 2 && y == 1)
            {
                return m.M32;
            }
            else if (x == 2 && y == 2)
            {
                return m.M33;
            }
            else if (x == 2 && y == 3)
            {
                return m.M34;
            }
            else if (x == 3 && y == 0)
            {
                return m.M41;
            }
            else if (x == 3 && y == 1)
            {
                return m.M42;
            }
            else if (x == 3 && y == 2)
            {
                return m.M43;
            }
            else if (x == 3 && y == 3)
            {
                return m.M44;
            }

            throw new InvalidOperationException("index is not valid");
        }

        public static Vector3 Multiply(this Matrix m, Vector3 v)
        {
            Vector3 r = Vector3.Zero;

            float invW = 1.0f / ((m.M41 * v.X) + (m.M42 * v.Y) + (m.M43 * v.Z) + m.M44);

            r.X = ((m.M11 * v.X) + (m.M12 * v.Y) + (m.M13 * v.Z) + m.M14) * invW;
            r.Y = ((m.M21 * v.X) + (m.M22 * v.Y) + (m.M23 * v.Z) + m.M24) * invW;
            r.Z = ((m.M31 * v.X) + (m.M32 * v.Y) + (m.M33 * v.Z) + m.M34) * invW;

            return r;
        }

        public static Vector4 Multiply(this Matrix m, Vector4 v)
        {
            return new Vector4(
                               (m.M11 * v.X) + (m.M12 * v.Y) + (m.M13 * v.Z) + (m.M14 * v.W),
                               (m.M21 * v.X) + (m.M22 * v.Y) + (m.M23 * v.Z) + (m.M24 * v.W),
                               (m.M31 * v.X) + (m.M32 * v.Y) + (m.M33 * v.Z) + (m.M34 * v.W),
                               (m.M41 * v.X) + (m.M42 * v.Y) + (m.M43 * v.Z) + (m.M44 * v.W));
        }

        public static Plane Multiply(this Matrix m, Plane p)
        {
            Plane ret = new Plane();
            Matrix invTrans = Matrix.Transpose(Matrix.Invert(m));
            Vector4 v4 = new Vector4(p.Normal.X, p.Normal.Y, p.Normal.Z, p.D);
            v4 = invTrans.Multiply(v4);
            ret.Normal = Vector3.Zero;
            ret.Normal.X = v4.X;
            ret.Normal.Y = v4.Y;
            ret.Normal.Z = v4.Z;
            ret.Normal.Normalize();
            ret.D = v4.W / ret.Normal.Length();
            return ret;
        }

        public static Matrix CreateViewMatrix(Vector3 position, Quaternion orientation)
        {
            return CreateViewMatrix(position, orientation, Matrix.Identity);
        }

        public static Matrix CreateViewMatrix(Vector3 position, Quaternion orientation, Matrix reflectMatrix)
        {
            Matrix viewMatrix = Matrix.Identity;

            Matrix rot = Matrix.CreateFromQuaternion(orientation);
            Matrix rotT = Matrix.Transpose(rot);
            Vector3 trans = (-rotT).Multiply(position);

            viewMatrix = rotT;
            viewMatrix.M14 = trans.X;
            viewMatrix.M24 = trans.Y;
            viewMatrix.M34 = trans.Z;

            if (reflectMatrix != Matrix.Identity)
            {
                viewMatrix = viewMatrix * reflectMatrix;
            }

            return viewMatrix;
        }
    }
}
